#pragma once

#if defined(USE_CPP_CHECK)
#define TXDNN_OBJECT_CAST reinterpret_cast
#else
#define TXDNN_OBJECT_CAST static_cast
#endif

#define TXDNN_OBJECT_DEFINE(obj, ...)                                   \
  inline __VA_ARGS__& txdnn_get_object(obj& o) {                        \
    return TXDNN_OBJECT_CAST<__VA_ARGS__&>(o);                          \
  }                                                                     \
  inline const __VA_ARGS__& txdnn_get_object(const obj& o) {            \
    return TXDNN_OBJECT_CAST<const __VA_ARGS__&>(o);                    \
  }                                                                     \
  inline void txdnn_destroy_object(obj* p) {                            \
    txdnn::detail::delete_object(TXDNN_OBJECT_CAST<__VA_ARGS__*>(p));   \
  }


namespace txdnn {

template <int N>
struct rank : rank<N - 1> {};

template <>
struct rank<0> {};


namespace detail {

template <typename T>
void delete_object(T* x) {
  delete x;
}

template <class T>
T& get_object_impl(rank<0>, T& x) { return x; }

template <class T>
auto get_object_impl(rank<1>, T& x) -> decltype(txdnn_get_object(x)) {
  return txdnn_get_object(x);
}

} // namespace detail

template <class T>
auto get_object(T& x) -> decltype(detail::get_object_impl(rank<1>{}, x)) {
  return detail::get_object_impl(rank<1>{}, x);
}

} // namespace txdnn